package com.budwk.app.sqfw.services.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.budwk.app.common.config.AppException;
import com.budwk.app.sqfw.constants.CommonConstants;
import com.budwk.app.sqfw.enums.SupportBusinessTypeEnum;
import com.budwk.app.sqfw.models.BuildingInfo;
import com.budwk.app.sqfw.models.HouseInfo;
import com.budwk.app.sqfw.models.PersonInfo;
import com.budwk.app.sqfw.services.BuildingInfoService;
import com.budwk.app.sqfw.services.HouseInfoService;
import com.budwk.app.sqfw.services.PersonInfoService;
import com.budwk.app.sqfwV2.component.HouseInfoComponent;
import com.budwk.app.sqfwV2.component.HouseholdComponent;
import com.budwk.app.sqfwV2.dto.dto.PersonInfoDTO;
import com.budwk.app.sqfwV2.dto.vo.*;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.sys.services.SysUserService;
import com.budwk.starter.common.page.Pagination;
import com.budwk.starter.common.result.Result;
import com.budwk.starter.database.service.BaseServiceImpl;
import com.budwk.starter.security.utils.SecurityUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@IocBean(args = {"refer:dao"})
@Slf4j
public class HouseInfoServiceImpl extends BaseServiceImpl<HouseInfo> implements HouseInfoService {
    public HouseInfoServiceImpl(Dao dao) {
        super(dao);
    }

    @Inject
    private SysUserService userService;
    @Inject
    private BuildingInfoService buildingInfoService;
    @Inject
    private PersonInfoService personInfoService;

    @Inject("taskQueryExecutor")
    private ThreadPoolExecutor taskQueryExecutor;

    @Override
    @Aop(TransAop.READ_COMMITTED)
    public HouseInfo submitData(HouseInfo reqVo) {
        // 1 校验提交基本参数
        HouseInfoComponent.checkSubmitData(reqVo);
        // 2 入库
        List<HouseRelatPerson> houseRelatPeoples = reqVo.getHouseRelatPeoples();
        reqVo.setHouseRelatPeoples(CollectionUtil.isEmpty(houseRelatPeoples) ? Lists.newArrayList() : houseRelatPeoples)
                .setUpdateTime(LocalDateTime.now()).setUpdateId(SecurityUtil.getUserId());
        this.updateIgnoreNull(reqVo);
        // 记录修改记录
        HouseholdComponent.insertRecord(JSONObject.toJSONString(reqVo), SupportBusinessTypeEnum.HOUSE_TYPE.getName(), reqVo.getDetailAddress());
        return reqVo;
    }

    @Override
    public HouseInfo findByDetailAddress(String detailAddress) {
        Cnd cnd = Cnd.NEW();
        SqlExpressionGroup where = cnd.where();
        where.andEquals(HouseInfo::getDetailAddress, detailAddress).andEquals(HouseInfo::isDeleted, false);
        return this.fetch(cnd);
    }

    @Override
    public void deleteData(HouseInfoReqVo reqVo) {
        // 1 判断房屋下是否存在人
        HouseInfo houseInfo = this.fetch(reqVo.getId());
        if (houseInfo == null) {
            throw new AppException("房屋信息不存在！");
        }
        if (CollectionUtil.isNotEmpty(houseInfo.getHouseRelatPeoples())) {
            // 存在人员数据 更新删除
            houseInfo.setDeleted(true).setDeleteReason(reqVo.getDeleteReason());
            this.updateIgnoreNull(houseInfo);
            return;
        }
        // 无任何数据 直接删除
        this.delete(reqVo.getId());
    }

    @Override
    public List<PersonInfo> listRelatePerson(PersonInfoReqVo reqVo) {
        Cnd cnd = Cnd.NEW();
        SqlExpressionGroup criSql = cnd.where();
        String queryCondition = reqVo.getQueryCondition();
        if (StringUtils.isNotEmpty(queryCondition)) {
            // 姓名 电话 身份证 地址
            SqlExpressionGroup a1 = Cnd.exps(PersonInfo::getName, "like", "%" + queryCondition + "%");
            SqlExpressionGroup a3 = Cnd.exps(PersonInfo::getIdNumber, "like", "%" + queryCondition + "%");
            criSql.and(a1.or(a3));
        }
        // 五级地址必须相同下才可以
        Sys_user user = userService.fetch(SecurityUtil.getUserId());
        criSql.andEquals(PersonInfo::getProvince, user.getProvince())
                .andEquals(PersonInfo::getCity, user.getCity())
                .andEquals(PersonInfo::getDistrict, user.getDistrict())
                .andEquals(PersonInfo::getCounty, user.getCounty())
                .andEquals(PersonInfo::getCommunity, user.getCommunity())
                .andEquals(PersonInfo::isDeleted, false);
        cnd.getOrderBy().desc(PersonInfo::getUpdateDate);
        return personInfoService.query(cnd);
    }

    /**
     * 初始化建立房屋信息
     * @param building 当前数据库存在的楼栋
     */
    @Override
    public void initSaveHouse(BuildingInfo reqParam, String userId, BuildingInfo building) {
        List<BuildingUnitVo> buildUnits = reqParam.getBuildUnits();
        if (CollectionUtil.isEmpty(buildUnits)) {
            return;
        }
        // 查询当前楼栋下是否已经建立了房屋
        List<HouseInfo> infos = findByBuildId(reqParam.getId());
        if (CollectionUtil.isEmpty(infos)) {
            // 没有建立房屋，需要初始化第一次建立房屋
            batchSaveHouse(reqParam, userId);
            return;
        }
        // 已经建立了房屋 需要新增房屋
        for (BuildingUnitVo buildUnit : buildUnits) {
            // 从原有单元中取出单元信息
            Optional<BuildingUnitVo> any = building.getBuildUnits().stream().filter(e -> e.getUnitName().equals(buildUnit.getUnitName())).findAny();
            if (any.isEmpty()) {
                // 原有单元中不存在该单元 添加数据
                saveHouse(buildUnit, reqParam, userId);
                continue;
            }
            // 原有单元中已经存在该房屋信息
            BuildingUnitVo unitVo = any.get(); // 原有数据的单元信息
            if (buildUnit.getFloorsCount() < unitVo.getFloorsCount()) {
                throw new AppException("单元层数不能小于当前单元层数！");
            }
            // 查询当前单元中的所有房子
            List<HouseInfo> houseInfos = queryFloorHouse(null, buildUnit.getUnitName(), reqParam.getId());
            Optional<HouseInfo> first = houseInfos.stream().filter(e -> CommonConstants.YES_CHINA.equals(e.getIsFill())).findFirst();
            if (first.isEmpty()) {
                // 数据未填报过 删除原有单元中的所有房子 新建房屋
                List<String> ids = houseInfos.stream().map(HouseInfo::getId).collect(Collectors.toList());
                this.delete(ids);
                saveHouse(buildUnit, reqParam, userId);
                continue;
            }
            // 数据有填报过
            // 每层户数不能小于原有的每层户数
            if (buildUnit.getFloorHouses() < unitVo.getFloorHouses()) {
                throw new AppException("数据有填报过，每层户数不能小于原有的每层户数！");
            }
            // 层数和每层户数一致 不需要修改
            if (buildUnit.getFloorsCount().equals(unitVo.getFloorsCount()) && buildUnit.getFloorHouses().equals(unitVo.getFloorHouses())) {
                continue;
            }
            // 更新房屋信息
            HouseInfoComponent.updateHouseInfo(buildUnit, userId, houseInfos, reqParam);
        }
    }

    private void batchSaveHouse(BuildingInfo reqParam, String userId) {
        for (BuildingUnitVo unitVo : reqParam.getBuildUnits()) {
            saveHouse(unitVo, reqParam, userId);
        }
    }

    private void saveHouse(BuildingUnitVo reqUnitInfo, BuildingInfo reqParam, String userId) {
        Integer floorHouses = reqUnitInfo.getFloorHouses(); // 每层户数  3
        Integer start = reqUnitInfo.getStartFloor();  // 起始楼层  -5
        Integer floorsCount = reqUnitInfo.getFloorsCount();  // 单元层数  1    -5+1-1
        Sys_user user = userService.fetch(userId);
        Integer highFloor = reqUnitInfo.getStartFloor() + floorsCount; // 最高层数 起始楼层+单元层数
        for (int i = start; i < highFloor; i++) {
            if (start == 0) {
                start ++; // 楼层不能为0层 默认+1层
                highFloor ++;
                continue;
            }
            for (int j = 1; j <= floorHouses; j++) {
                HouseInfoReqVo reqVo = new HouseInfoReqVo();
                reqVo.setFloor(String.valueOf(start)).setUnit(reqUnitInfo.getUnitName());
                HouseInfo houseInfo = HouseInfoComponent.buildHouseInfo(reqVo, reqParam, user, j);
                // 入库操作
                this.insert(houseInfo);
            }
            start ++;
        }
    }

    private List<HouseInfo> queryFloorHouse(String floor, String unit, String buildId) {
        Cnd cnd = Cnd.NEW();
        SqlExpressionGroup where = cnd.where();
        where.andEquals(HouseInfo::getBuildingId, buildId);
        if (StringUtils.isNotEmpty(unit)) {
            where.andEquals(HouseInfo::getUnit, unit);
        }
        if (StringUtils.isNotEmpty(floor)) {
            where.andEquals(HouseInfo::getFloor, floor);
        }
        return this.query(cnd);
    }


    @Override
    @Aop(TransAop.READ_COMMITTED)
    public void updateFloor(HouseInfoReqVo reqVo) {
        // 查询当前单元楼层下的楼层信息
        List<HouseInfo> oldHouse = queryFloorHouse(reqVo.getOldFloor(), reqVo.getUnit(), reqVo.getBuildingId()); // 获取原来的房屋信息
        Sys_user user = userService.fetch(SecurityUtil.getUserId());
        BuildingInfo buildingInfo = buildingInfoService.fetch(reqVo.getBuildingId());
        List<HouseInfo> houseInfos = queryFloorHouse(reqVo.getFloor(), reqVo.getUnit(), reqVo.getBuildingId()); // 当前提交的楼层房屋查询信息
        if (houseInfos.size() > 0) {
            if (!reqVo.getOldFloor().equals(reqVo.getFloor())) {
                // 不是同一楼层 说明被占用 不能修改
                throw new AppException("楼层已被占用，不能修改！");
            }
        }
        // 检查原来的房间是否填报过数据
        Optional<HouseInfo> first = oldHouse.stream().filter(e -> CommonConstants.YES_CHINA.equals(e.getIsFill())).findFirst();
        if (first.isPresent()) {
            if (reqVo.getFloorHouses() < oldHouse.size()) {
                throw new AppException("存在房屋填报信息，楼层户数不能小于当前户数！");
            }
        }
        // 1 楼层不存在 初始化楼层信息
        // 修改原有的房间号和楼层
        int index = 1;
        for (HouseInfo houseInfo : oldHouse) {
            String houseNumber = String.format("%s%s", reqVo.getFloor(), houseInfo.getRoomNumber().substring(reqVo.getOldFloor().length()));// 房屋号
            houseInfo.setFloor(reqVo.getFloor()).setHouseNumber(houseNumber).setRoomNumber(houseNumber);
            this.updateIgnoreNull(houseInfo);
            index++;
        }
        // 新增不存在的房间
        int cz = reqVo.getFloorHouses() - oldHouse.size(); // 需要修改的差值
        for (int j = 0; j < cz; j++) {
            HouseInfo houseInfo = HouseInfoComponent.buildHouseInfo(reqVo, buildingInfo, user, index);
            this.insert(houseInfo);
            index ++;
        }
    }

    @Override
    public Result<Pagination> listBuildHouses(HouseInfoReqVo reqVo) {
        Cnd cnd = Cnd.NEW();
        SqlExpressionGroup where = cnd.where();
        // 关键词查询 小区名称 楼栋名称  网格
        if (StringUtils.isNotEmpty(reqVo.getQueryValue())) {
            SqlExpressionGroup a1 = Cnd.exps(BuildingInfo::getResidentialArea, "like", "%" + reqVo.getQueryValue() + "%");
            SqlExpressionGroup a2 = Cnd.exps(BuildingInfo::getBuildingName, "like", "%" + reqVo.getQueryValue() + "%");
            SqlExpressionGroup a3 = Cnd.exps(BuildingInfo::getGrid, "like", "%" + reqVo.getQueryValue() + "%");
            where.and(a1.or(a2).or(a3));
        }
        List<QueryCondititonVo> queryCondititons = reqVo.getQueryCondititons();
        if (CollectionUtil.isNotEmpty(queryCondititons)) {
            for (QueryCondititonVo condititonVo : queryCondititons) {
                if (StringUtils.isNotEmpty(condititonVo.getKey()) && StringUtils.isNotEmpty(condititonVo.getValue())) {
                    if (condititonVo.getKey().equals("floorsCount")) {
                        if (condititonVo.getValue().equals("1-8层")) {
                            where.andBetween(condititonVo.getKey(), 1, 8);
                            continue;
                        }
                        if (condititonVo.getValue().equals("8-30层")) {
                            where.andBetween(condititonVo.getKey(), 8, 30);
                            continue;
                        }
                        if (condititonVo.getValue().equals("30层以上")) {
                            where.andGT(condititonVo.getKey(), 30);
                            continue;
                        }
                        continue;
                    }
                    where.andEquals(condititonVo.getKey(), condititonVo.getValue());
                }
            }
        }
        // 检查用户数据查询权限
        HouseholdComponent.handleUserQueryAuth(where);
        where.andEquals(BuildingInfo::isDeleted, false);
        cnd.getOrderBy().desc(BuildingInfo::getUpdateTime);
        // 分页查询
        Pagination pagination = buildingInfoService.listPage(reqVo.getPageNo(), reqVo.getPageSize(), cnd);
        int totalCount = pagination.getTotalCount();
        if (totalCount > 0) {
            // 导入的数据一般没有详细地址 需要初始化更新
            List<BuildingInfo> list = pagination.getList(BuildingInfo.class);
            CompletableFuture.runAsync(() -> HouseholdComponent.initUpdateBuildInfo(list), taskQueryExecutor);
        }
        return Result.data(pagination);
    }

    @Override
    public List<HouseInfo> findByBuildId(String buildId) {
        Cnd cnd = Cnd.NEW();
        SqlExpressionGroup where = cnd.where();
        where.andEquals(HouseInfo::getBuildingId, buildId).andEquals(HouseInfo::isDeleted, false);
        cnd.getOrderBy().desc(HouseInfo::getUpdateTime);
        return this.query(cnd);
    }

    @Override
    @Aop(TransAop.READ_COMMITTED)
    public void updatePersonHouse(PersonInfoDTO personInfoDTO) {
        List<PersonHouseVo> personHouseVos = personInfoDTO.getHouses();
        if (CollectionUtil.isEmpty(personHouseVos)) {
            // 未提交关联的房屋信息
            return;
        }
        for (PersonHouseVo personHouseVo : personHouseVos) {
            HouseInfo houseInfo = getUpdateHouse(personHouseVo, personInfoDTO);
            if (CommonConstants.NO_CHINA.equals(houseInfo.getIsFill())) {
                houseInfo.setIsFill(CommonConstants.YES_CHINA).setFillDate(LocalDateTime.now());
            }
            this.updateIgnoreNull(houseInfo);
        }
    }

    private HouseInfo getUpdateHouse(PersonHouseVo personHouseVo, PersonInfoDTO personInfo) {
        String houseId = personHouseVo.getId(); // 房屋id
        HouseInfo houseInfo = this.fetch(houseId);
        List<HouseRelatPerson> houseRelatPeoples = houseInfo.getHouseRelatPeoples();
        if (CollectionUtil.isEmpty(houseRelatPeoples)) {
            // 该房屋从未关联过任何人 将当前用户添加至该房屋下
            houseRelatPeoples = new ArrayList<>();
            HouseRelatPerson person = new HouseRelatPerson();
            person.setPersonName(personInfo.getName())
                    .setPersonType(personHouseVo.getPersonType())
                    .setIdNumber(personInfo.getIdNumber())
                    .setPersonId(personInfo.getId());
            houseRelatPeoples.add(person);
            houseInfo.setHouseRelatPeoples(houseRelatPeoples);
            return houseInfo;
        }
        // 该房屋下有其他人，但是不存在这个人
        Optional<HouseRelatPerson> optional = houseRelatPeoples.stream().filter(e -> e.getPersonId().equals(personInfo.getId())).findFirst();
        if (optional.isEmpty()) {
            HouseRelatPerson person = new HouseRelatPerson();
            person.setPersonName(personInfo.getName())
                    .setPersonType(personHouseVo.getPersonType())
                    .setIdNumber(personInfo.getIdNumber())
                    .setPersonId(personInfo.getId());
            houseRelatPeoples.add(person);
            houseInfo.setHouseRelatPeoples(houseRelatPeoples);
            return houseInfo;
        }
        // 该房屋下已经有这个人存在 更新他的信息
        if (CommonConstants.YES_CHINA.equals(personHouseVo.getDeleted())) {
            // 需要删除该房屋下当前用户
            houseRelatPeoples.removeIf(e -> e.getPersonId().equals(personInfo.getId()));
            return houseInfo;
        }
        // 不是删除 需要更新
        HouseRelatPerson person = optional.get();
        person.setPersonType(personHouseVo.getPersonType());
        return houseInfo;
    }

    @Override
    public Result<HouseInfoResVo> listAllHouseByBuildId(String buildingId) {
        if (StringUtils.isEmpty(buildingId)) {
            throw new AppException("请传递参数id!");
        }
        List<HouseInfo> list = findByBuildId(buildingId);
        if (list.size() == 0) {
            return Result.success();
        }
        BuildingInfo buildingInfo = buildingInfoService.fetch(buildingId);
        // 获取所有的单元信息
        List<String> units = list.stream().map(HouseInfo::getUnit).distinct().collect(Collectors.toList());
        // 数据返回对象
        HouseInfoResVo infoResVo = new HouseInfoResVo();
        List<HouseInfoResVo.Houses> houses = new ArrayList<>();
        for (String unit : units) {
            // 按照单元分组获取信息
            HouseInfoResVo.Houses house = new HouseInfoResVo.Houses();
            // 获取所有的楼层
            List<String> floors = list.stream().filter(e -> e.getUnit().equals(unit)).map(HouseInfo::getFloor).distinct().collect(Collectors.toList());
            List<HouseInfoResVo.Units> unitList = new ArrayList<>();
            for (String floor : floors) {
                // 解析所有的楼层信息
                HouseInfoResVo.Units resUnits = new HouseInfoResVo.Units();
                // 获取当前楼层的所有房间号
                List<HouseInfo> rooms = list.stream().filter(e -> e.getUnit().equals(unit) && floor.equals(e.getFloor())).collect(Collectors.toList());
                // 组装房间信息
                List<HouseInfoResVo.Rooms> resRooms = new ArrayList<>();
                rooms.forEach(houseInfo -> resRooms.add(buildRoom(houseInfo, house, infoResVo, buildingInfo)));
                // 按照房间号升序
                resRooms.sort(Comparator.comparing(HouseInfoResVo.Rooms::getHouseRoom));  // 升序
                resUnits.setRooms(resRooms).setFloorName(floor);
                unitList.add(resUnits);
            }
            house.setUnits(sortUnits(unitList)).setUnitName(unit);
            houses.add(house);
        }
        infoResVo.setHouses(houses).setBuildingId(buildingId);
        return Result.data(infoResVo);
    }

    private static List<HouseInfoResVo.Units> sortUnits(List<HouseInfoResVo.Units> unitList) {
        // 记录是纯数字的楼层
        List<HouseInfoResVo.Units> unitList1 = new ArrayList<>();
        // 记录是纯字符的楼层
        List<HouseInfoResVo.Units> unitList2 = new ArrayList<>();
        for (HouseInfoResVo.Units units : unitList) {
            if (StringUtils.isNumeric(units.getFloorName())) {
                unitList1.add(units);
            } else {
                unitList2.add(units);
            }
        }
        unitList1.sort(Comparator.comparing(o -> Integer.parseInt(o.getFloorName())));
        List<HouseInfoResVo.Units> res = new ArrayList<>();
        res.addAll(unitList1);
        res.addAll(unitList2);
        return res;
    }

    @Override
    public HouseInfo getHouseDetail(String houseId) {
        HouseInfo houseInfo = this.fetch(houseId);
        if (houseInfo == null) {
            return null;
        }
        String buildingId = houseInfo.getBuildingId();
        BuildingInfo b = buildingInfoService.fetch(buildingId);
        // 组装数据返回
        houseInfo.setBuildingInfo(b);
        return houseInfo;
    }


    private HouseInfoResVo.Rooms buildRoom(HouseInfo houseInfo, HouseInfoResVo.Houses house, HouseInfoResVo infoResVo, BuildingInfo buildingInfo) {
        HouseInfoResVo.Rooms room = new HouseInfoResVo.Rooms();
        room.setRooms(houseInfo.getRoomNumber())
                .setHouseRisks(houseInfo.getHouseRisks())
                .setIsFill(StringUtils.isNotEmpty(houseInfo.getIsFill()) ? houseInfo.getIsFill() : CommonConstants.NO_CHINA)
                .setIsLoft(houseInfo.getIsLoft())
                .setHouseRoom(houseInfo.getHouseNumber())
                .setAddress(houseInfo.getDetailAddress())
                .setHouseId(houseInfo.getId());
        if (StringUtils.isNotEmpty(houseInfo.getHouseRisks()) && CommonConstants.YES_CHINA.equals(houseInfo.getHouseRisks())) {
            house.setHasDanger(CommonConstants.YES_CHINA);
            infoResVo.setHouseRisks(CommonConstants.YES_CHINA);
        }
        if (StringUtils.isNotEmpty(houseInfo.getIsFill()) && CommonConstants.YES_CHINA.equals(houseInfo.getIsFill())) {
            house.setIsFill(CommonConstants.YES_CHINA);
            infoResVo.setIsFill(CommonConstants.YES_CHINA);
        }
        CompletableFuture.runAsync(() -> HouseholdComponent.initUpdateHouseInfo(List.of(houseInfo), buildingInfo), taskQueryExecutor);
        return room;
    }
}
